package sns.service;

import java.util.ArrayList;
import java.util.List;
import org.springframework.transaction.annotation.Transactional;
import sns.bean.Impression;
import sns.bean.Tag;
import sns.bean.User;
import sns.dao.ImpressionDao;
import sns.dao.RelationDao;
import sns.dao.TagDao;
import sns.util.LevenshteinDistance;
import sns.util.RecommendUser;

@Transactional
public class RecommendService {
	
	private RelationDao relationDao;
	private ImpressionDao impressionDao;
	private TagDao tagDao;
	
	// constants
	private final float DIF = 0.6f;
	
	public List<RecommendUser> recommendUsers(User user, int n){
		// check parameters
		if(user==null||n<=0){
			return null;
		}
		// return recommended users
		List<RecommendUser> rec_users = new ArrayList<RecommendUser>(n);
		// the tags and impressions of the user
		List<Tag> tags = tagDao.getTagsOfUser(user);
		List<Impression> impressions = impressionDao.getImpressionsOfUser(user);
		// tmp variables user in the loop
		List<User> users = null;
		List<Tag> tmpTags = null;
		List<Impression> tmpImpressions = null;
		Tag tmpTag = null;
		Impression tmpImpression = null;
		// the number of users to fetch in each loop
		int delta = 4*n; 
		// the min and max of fetched users
		int min = 0;
		int max = min+delta;
		// status indicating whether to check tags or impressions
		boolean checkTag = tags.size()!=0;
		boolean checkImpression = impressions.size()!=0;
		// if tags and impressions are both empty
		if(!checkTag&&!checkImpression){
			return rec_users;
		}
		// main loop
		do{
			// get potential users
			users = relationDao.getHotUsers(min, max);
			for(User tmpUser:users){
				if(rec_users.size()==n){
					break;
				}
				// skip the user himself
				if(user.getIduser().equals(tmpUser.getIduser())){
					continue;
				}
				// skip the user's idols
				if(relationDao.checkIfFollow(user, tmpUser)){
					continue;
				}
				if(checkTag){
					tmpTags = tagDao.getTagsOfUser(tmpUser);
					tmpTag = checkTagRecommend(tags,tmpTags);
					if(tmpTags.size()>0&&tmpTag!=null){
						rec_users.add(new RecommendUser(tmpUser, tmpTag.getTagtxt(), RecommendUser.REASON_TAG));
						continue;
					}
				}
				if(checkImpression){
					tmpImpressions = impressionDao.getImpressionsOfUser(tmpUser);
					tmpImpression = checkImpressionRecommend(impressions,tmpImpressions);
					if(tmpImpressions.size()>0&&tmpImpression!=null){
						rec_users.add(new RecommendUser(tmpUser, tmpImpression.getImpression(), RecommendUser.REASON_IMPRESSION));
						continue;
					}
				}
			}
			min = max;
			max = min+delta;
		}while(rec_users.size()<n&&users.size()==delta);
		return rec_users;
	}

	private Impression checkImpressionRecommend(List<Impression> impressions,
			List<Impression> tmpImpressions) {
		for(Impression i:impressions){
			for(Impression j:tmpImpressions){
				if(LevenshteinDistance.levenshtein(i.getImpression(), j.getImpression())>=DIF){
					return j;
				}
			}
		}
		return null;
	}

	private Tag checkTagRecommend(List<Tag> tags, List<Tag> tmpTags) {
		for(Tag i:tags){
			for(Tag j:tmpTags){
				if(LevenshteinDistance.levenshtein(i.getTagtxt(), j.getTagtxt())>=DIF){
					return j;
				}
			}
		}
		return null;
	}

	public void setRelationDao(RelationDao relationDao) {
		this.relationDao = relationDao;
	}

	public void setImpressionDao(ImpressionDao impressionDao) {
		this.impressionDao = impressionDao;
	}

	public void setTagDao(TagDao tagDao) {
		this.tagDao = tagDao;
	}

}
